#include "GameEngine.h"

bool GameEngine::authorityIsInvalid(Authority* creds)
{
	return (creds != participantCredentials);
} // end method

void GameEngine::begin()
{
	if (isUnderway())
		throw GameNotReady("Game in progress must end before one may begin.");

	if (minimumNumberOfPlayers() > numberOfSeatedPlayers)
		throw GameNotReady("Minimum attendance not achieved.");

	// TODO AttendanceListener events

	currentState = GameState::INITIAL->getNextSequence();
	goadPlayers();
} // end method

void GameEngine::enact(
	GameState* state,
	Authority* credential
) {
	if (authorityIsInvalid(credential))
		throw std::invalid_argument("State transition refused.  Credentials expired or unknown.");

	{
		StateList& validStates = currentState->listAvailableActions();
		bool stateIsInvalid = true;
		for (StateList::iterator i = validStates.begin(); i != validStates.end(); ++i)
			if (*i == state)
				stateIsInvalid = false;
		if (stateIsInvalid)
			throw std::invalid_argument("State transition refused.  Action isn't available from this node.");
	} // end block: validate state parameter

	for (GamePlayer** p = participants + numberOfSeatedPlayers; p-- != participants;)
		(*p)->observeStateTransition(credential->getLocation(), currentState, state);

	currentState->transitionTo(state);
	state->transitionFrom(currentState);
	currentState = state;

	if (currentState->isLeaf())
		endSequence();
	else
		goadPlayers();
} // end method

void GameEngine::end()
{
	// TODO can't think of anything to do here yet
} // end method

SeatNumber GameEngine::findEmptySeat()
{
	int seat = -1;
	while (numberOfSeatedPlayers != ++seat)
		if (NULL == participants[seat])
			return seat;

	return -1; // TODO: Might want to throw an exception instead.
} // end method

void GameEngine::goadPlayers()
{
	GamePlayer* currentPlayer = currentState->getActivePlayer();
	unsigned int location = locateParticipant(currentPlayer);

	currentPlayer->startTurn(
		resetCredentials(
			location,
			new Authority(location)
		)
	);
} // end method

void GameEngine::introducePlayer(SeatNumber seat)
{
	AttendanceList::iterator i = attendanceListeners.begin();
	AttendanceList::iterator const end = attendanceListeners.end();
	while (end != i)
		(*i)->participantIntroduced(seat, participants[seat]);
} // end method

SeatNumber GameEngine::locateParticipant(GamePlayer* presumedParticipant)
{
	auto seatNumber = maximumNumberOfPlayers();

	for (GamePlayer** actualParticipant = participants + seatNumber;
		--seatNumber, actualParticipant-- != participants;
	)
		if (*actualParticipant == presumedParticipant)
			return seatNumber;

	throw std::runtime_error("Player not found by locateParticipant().");
} // end method

SeatNumber GameEngine::minimumNumberOfPlayers()
{
	return 1;
} // end method

SeatNumber GameEngine::maximumNumberOfPlayers()
{
	return 4;
} // end method

Authority* GameEngine::resetCredentials(
	SeatNumber	seat,
	Authority*	newCreds
){

#ifdef GAME_ENGINE_PERMITS_SIMULTANEOUS_TURNS

	if (ALL_SEATS == seat)
		for (int seat = maximumNumberOfPlayers(); seat-- != 0;)
			resetCredentials(seat, newCreds);

	Authority*&	currentCreds = participantCredentials[seat];
	if (NULL != currentCreds)
		delete currentCreds;

	currentCreds = newCreds;

#else

	if (NULL != participantCredentials)
		delete participantCredentials;

	participantCredentials = newCreds;

#endif

	return newCreds;
} // end method

void GameEngine::seat(
	GamePlayer* who
) {
	if (maximumNumberOfPlayers() == numberOfSeatedPlayers)
		throw GameNotReady("Game already holds its maximum number of players.");

	++numberOfSeatedPlayers;
	auto seatNumber = findEmptySeat();

	participants[seatNumber] = who;
	introducePlayer(seatNumber);
} // end method

void GameEngine::vacate(SeatNumber seatNumber)
{
	if (maximumNumberOfPlayers() <= seatNumber)
		throw std::out_of_range("Seat number exceeds maximum index.");

	GamePlayer* who = participants[seatNumber];
	if (NULL == who)
		throw std::invalid_argument("The seat at that index is unoccupied.");

	participants[seatNumber] = NULL;

	AttendanceList::iterator i = attendanceListeners.begin();
	AttendanceList::iterator const end = attendanceListeners.end();
	while (end != i)
		(*i)->participantLeft(seatNumber, who);
} // end method
